#! /usr/bin/python3
# -*- coding: UTF-8 -*-

if __name__ == "__main__":
	raise Exception("不支持从这里调用")

__version__ = "1.0"
__all__ = []

from hashlib import md5
from uuid import uuid4

from com.mousechsh.business.lib.model.MouseChshAppModel import MouseChshAppModel
from com.mousechsh.business.lib.MouseChshAuthorizationAnnotition import MouseChshAuthorizationAnnotition
from com.mousechsh.business.lib.model.MouseChshDomainModel import MouseChshDomainModel
from com.mousechsh.business.lib.model.MouseChshKeyValueModel import MouseChshKeyValueModel
from com.mousechsh.business.lib.model.MouseChshLoginModel import MouseChshLoginModel
from com.mousechsh.business.lib.model.MouseChshModuleModel import MouseChshModuleModel
from com.mousechsh.business.lib.model.MouseChshUserModel import MouseChshUserModel
from com.mousechsh.common.data.MouseChshDao import MouseChshDao
from com.mousechsh.common.data.json.MouseChshJson import mousechsh_json_from_json, mousechsh_json_to_json
from com.mousechsh.common.io.cache.MouseChshRedisCache import MouseChshRedisCache
from com.mousechsh.common.io.db.MouseChshPostgresql import MouseChshPostgresql
from com.mousechsh.common.middle.auth.MouseChshJwt import mousechsh_jwt_encode_string
from com.mousechsh.common.middle.conf.MouseChshConfiguration import mousechsh_configuration_get
from com.mousechsh.common.middle.http.MouseChshHttpHeader import mousechsh_http_header_accept_language_annotation, \
	mousechsh_http_header_authorization_annotation
from com.mousechsh.common.middle.rest_api.MouseChshRestApi import mousechsh_rest_api_annotation
from com.mousechsh.common.middle.router.MouseChshCorsAnnotition import mousechsh_cors_annotation
from com.mousechsh.common.middle.router.MouseChshHttpRouter import mousechsh_http_router_annotation
from com.mousechsh.common.util.MouseChshDateTime import MouseChshDateTime


@mousechsh_http_router_annotation(method='GET', path='/')
@mousechsh_http_header_accept_language_annotation()
def index(url, request, response, *argsArr, **argsDict):
	response.get_header().set_content_type('html')
	response.set_body(
		'''
	   <!DOCTYPE html>
	   <html lang="zh-CN">
	   <head>
		   <meta charset="UTF-8">
		   <meta http-equiv="X-UA-Compatible" content="IE=edge">
		   <meta name="viewport" content="width=device-width, initial-scale=1.0">
		   <title>认证服务器</title>
	   </head>
	   <body>
		   认证服务器已经启动，请通过接口访问本服务。
	   </body>
	   </html>
	   '''
	)


@mousechsh_http_router_annotation(method=['POST', 'OPTIONS'], path='/login')
@mousechsh_cors_annotation()
@mousechsh_http_header_accept_language_annotation()
@mousechsh_rest_api_annotation()
def login(params):
	user = params.get('UserName', None)
	domain = params.get('Domain', None)
	app = params.get('App', None)
	password = params.get('Password', None)

	loginAlive = mousechsh_configuration_get('token.login.alive', 3600)

	loginMode = mousechsh_configuration_get('login.mode', 'each')

	if user is None:
		return None
	if domain is None:
		return None
	if app is None:
		return None

	db = MouseChshPostgresql()
	db.connect()
	data = db.get(
		'''
	   SELECT
		   A."C_Id",
		   A."C_Name"
	   FROM "A_Application"."T_App" A
	   INNER JOIN "A_AD"."T_Domain" B
		   ON A."L_Domain_Id" = B."C_Id"
	   WHERE A."C_St"         = 0
	   AND B."C_St"           = 0
	   AND A."C_Name"         = %(app)s
	   AND B."C_Name"         = %(domain)s
		   ''', {
			'app': app,
			'domain': domain
		}
	)
	db.close()

	dao = MouseChshDao()
	dao.set_model(MouseChshAppModel)
	apps = dao.parse(data)
	if len(apps) <= 0:
		return None
	app = apps[0]

	cache = MouseChshRedisCache()
	cache.connect()
	cache.set('MousechshApp:Id=' + app.get('Id'), mousechsh_json_to_json(app), loginAlive)
	cache.close()

	db = MouseChshPostgresql()
	db.connect()
	data = db.get(
		'''
	   SELECT
		   A."C_Id",
		   A."C_Name",
		   A."L_Domain_Id"  AS "C_Domain_Id",
		   A."L_AuthMethod",
		   A."C_Password"
	   FROM "A_AD"."T_User" A
	   INNER JOIN "A_AD"."T_Domain" B
		   ON A."L_Domain_Id" = B."C_Id"
	   WHERE A."C_St"         = 0
	   AND B."C_St"           = 0
	   AND A."C_Name"         = %(user)s
	   AND B."C_Name"         = %(domain)s
		   ''', {
			'user': user,
			'domain': domain
		}
	)
	db.close()

	dao = MouseChshDao()
	dao.set_model(MouseChshUserModel)
	dao.set_extra_model(MouseChshDomainModel)
	users = dao.parse(data)
	if len(users) <= 0:
		return None
	user = users[0]

	if user.get('AuthMethod') == 'NONE':
		pass
	elif user.get('AuthMethod') == 'TXT':
		if user.get('Password') != password:
			return None
	elif user.get('AuthMethod') == 'MD5':
		if user.get('Password').upper() != md5(password.encode('utf-8')).hexdigest().upper():
			return None

	login = MouseChshLoginModel()
	login.Id = str(uuid4()).upper()
	login.Code = str(uuid4()).upper()
	login.User = user.get('Id')
	login.Domain = user.get('Domain')
	login.App = app.get('Id')
	login.LoginTime = MouseChshDateTime().to_timestamp()
	login.Alive = loginAlive
	login.ExpTime = login.get('LoginTime') + login.get('Alive')

	db = MouseChshPostgresql()
	db.connect()
	if loginMode == 'each':
		db.execute(
			'''
		   DELETE FROM "A_Application"."T_Login"
		   WHERE "C_Id" = %(id)s
				   ''', {
				'id': login.get('Id')
			}
		)
	elif loginMode == 'user':
		db.execute(
			'''
		   DELETE FROM "A_Application"."T_Login"
		   WHERE "L_User_Id" = %(user)s
				   ''', {
				'user': login.get('User')
			}
		)
	elif loginMode == 'user-app':
		db.execute(
			'''
		   DELETE FROM "A_Application"."T_Login"
		   WHERE "L_User_Id" = %(user)s
		   AND   "L_App_Id"  = %(app)s
				   ''', {
				'user': login.get('User'),
				'app': login.get('App')
			}
		)
	db.execute(
		'''
	   INSERT INTO "A_Application"."T_Login"(
		   "C_Id",
		   "C_Code",
		   "L_User_Id",
		   "L_Domain_Id",
		   "L_App_Id",
		   "C_LoginTime",
		   "C_Alive",
		   "C_ExpTime"
	   )VALUES(
		   %(id)s,
		   %(code)s,
		   %(user)s,
		   %(domain)s,
		   %(app)s,
		   %(logintime)s,
		   %(alive)s,
		   %(exptime)s
	   )
		   ''', {
			'id': login.get('Id'),
			'code': login.get('Code'),
			'user': login.get('User'),
			'domain': login.get('Domain'),
			'app': login.get('App'),
			'logintime': login.get('LoginTime'),
			'alive': login.get('Alive'),
			'exptime': login.get('ExpTime')
		}
	)
	db.commit()
	db.close()

	cache = MouseChshRedisCache()
	cache.connect()
	if loginMode == 'each':
		cache.set('MousechshLogin:Id=' + login.get('Id'), mousechsh_json_to_json(login), loginAlive)
	elif loginMode == 'user':
		cache.set('MousechshLogin:User=' + login.get('User'), mousechsh_json_to_json(login), loginAlive)
	elif loginMode == 'user-app':
		cache.set(
			'MousechshLogin:User=' + login.get('User') + ',App=' + login.get('App'),
			mousechsh_json_to_json(login), loginAlive
		)
	cache.close()

	login.remove('Code')
	login.remove('LoginTime')
	login.remove('Alive')
	login.remove('ExpTime')
	jwtString = mousechsh_jwt_encode_string(login, login.get('App'), exp=loginAlive)

	return {
		'Token': login.get('Id'),
		'Auth': jwtString
	}


@mousechsh_http_router_annotation(method=['POST', 'OPTIONS'], path='/logout')
@mousechsh_cors_annotation()
@mousechsh_http_header_accept_language_annotation()
@MouseChshAuthorizationAnnotition()
@mousechsh_rest_api_annotation()
def logout(params):
	auth = params['auth']
	login = MouseChshLoginModel()
	login.set_data(auth['data'])

	cache = MouseChshRedisCache()
	cache.connect()
	cache.remove('MousechshLogin:Id=' + login.get('Id'))
	cache.remove('MousechshLogin:User=' + login.get('User'))
	cache.remove('MousechshLogin:User=' + login.get('User') + ',App=' + login.get('App'))
	cache.close()

	db = MouseChshPostgresql()
	db.connect()
	db.execute(
		'''
	   DELETE FROM "A_Application"."T_Login"
	   WHERE "C_Id"   = %(id)s
	   OR "L_User_Id" = %(user)s
		   ''', {
			'id': login.get('Id'),
			'user': login.get('User')
		}
	)
	db.commit()
	db.close()

	return True


@mousechsh_http_router_annotation(method=['POST', 'OPTIONS'], path='/token/check')
@mousechsh_cors_annotation()
@mousechsh_http_header_accept_language_annotation()
@mousechsh_http_header_authorization_annotation()
@mousechsh_rest_api_annotation()
def checkToken(params):
	auth = params['auth']
	login = MouseChshLoginModel()
	login.set_data(auth['data'])

	loginMode = mousechsh_configuration_get('login.mode', 'each')

	cache = MouseChshRedisCache()
	cache.connect()
	if loginMode == 'each':
		cacheobj = cache.get('MousechshLogin:Id=' + login.get('Id'))
	elif loginMode == 'user':
		cacheobj = cache.get('MousechshLogin:User=' + login.get('User'))
	elif loginMode == 'user-app':
		cacheobj = cache.get('MousechshLogin:User=' + login.get('User') + ',App=' + login.get('App'))
	cache.close()

	if cacheobj is not None:
		cacheobj = mousechsh_json_from_json(cacheobj)
	if cacheobj is not None:
		return True

	db = MouseChshPostgresql()
	db.connect()
	data = db.get(
		'''
	   SELECT
		   "C_Id",
		   "C_Code",
		   "L_User_Id"   AS "C_User_Id",
		   "L_Domain_Id" AS "C_Domain_Id",
		   "L_App_Id"    AS "C_App_Id",
		   "C_LoginTime",
		   "C_Alive",
		   "C_ExpTime"
	   FROM "A_Application"."T_Login"
	   WHERE "C_Id"     = %(id)s
	   AND "C_ExpTime" >= %(exp)s
		   ''', {
			'id': login.get('Id'),
			'exp': MouseChshDateTime().to_timestamp()
		}
	)
	db.close()

	dao = MouseChshDao()
	dao.set_model(MouseChshLoginModel)
	logins = dao.parse(data)
	if len(logins) <= 0:
		cache = MouseChshRedisCache()
		cache.connect()
		if loginMode == 'each':
			cache.remove('MousechshLogin:Id=' + login.get('Id'))
		elif loginMode == 'user':
			cache.remove('MousechshLogin:User=' + login.get('User'))
		elif loginMode == 'user-app':
			cache.remove('MousechshLogin:User=' + login.get('User') + ',App=' + login.get('App'))
		cache.close()
		return False

	login = logins[0]
	cache = MouseChshRedisCache()
	cache.connect()
	if loginMode == 'each':
		cache.set(
			'MousechshLogin:Id=' + login.get('Id'), mousechsh_json_to_json(login),
			login.get('ExpTime') - MouseChshDateTime().to_timestamp()
		)
	elif loginMode == 'user':
		cache.set(
			'MousechshLogin:User=' + login.get('User'), mousechsh_json_to_json(login),
			login.get('ExpTime') - MouseChshDateTime().to_timestamp()
		)
	elif loginMode == 'user-app':
		cache.set(
			'MousechshLogin:User=' + login.get('User') + ',App=' + login.get('App'),
			mousechsh_json_to_json(login), login.get('ExpTime') - MouseChshDateTime().to_timestamp()
		)
	cache.close()
	return True


@mousechsh_http_router_annotation(method=['POST', 'OPTIONS'], path='/user/current')
@mousechsh_cors_annotation()
@mousechsh_http_header_accept_language_annotation()
@MouseChshAuthorizationAnnotition()
@mousechsh_rest_api_annotation()
def getUser(params):
	lang = params.get('lang', 'zh-cn')
	auth = params.get('auth', None)

	if auth is None:
		return None
	login = auth.get('data', None)
	if login is None:
		return None

	cache = MouseChshRedisCache()
	cache.connect()
	cacheobj = cache.get('MousechshUser:Id=' + login.get('User') + ',Current[' + lang + ']')
	cache.close()

	if cacheobj is not None:
		cacheobj = mousechsh_json_from_json(cacheobj)
	if cacheobj is not None:
		return cacheobj

	loginAlive = mousechsh_configuration_get('token.login.alive', 3600)

	db = MouseChshPostgresql()
	db.connect()
	data = db.get(
		'''
	   SELECT
		   A."C_Id",
		   A."C_Name",
		   A."L_Domain_Id" AS "C_Domain_Id",
		   A."L_Domain_Id",
		   B."C_Name"      AS "L_Domain_Name",
		   COALESCE( A."X_Display" ->> %(lang)s, A."X_Display" ->> ( A."X_Display" ->> '' ) ) AS "C_Display",
		   COALESCE( B."X_Display" ->> %(lang)s, B."X_Display" ->> ( B."X_Display" ->> '' ) ) AS "L_Domain_Display",
		   A."C_NeekName",
		   A."C_Email"
	   FROM "A_AD"."T_User" A
	   INNER JOIN "A_AD"."T_Domain" B
		   ON A."L_Domain_Id" = B."C_Id"
	   WHERE A."C_Id"         = %(id)s
	   AND A."C_St"           = 0
	   AND B."C_St"           = 0
	   LIMIT 100
		   ''', {
			'id': login.get('User'),
			'lang': lang
		}
	)
	db.close()

	dao = MouseChshDao()
	dao.set_model(MouseChshUserModel)
	dao.set_extra_model(MouseChshDomainModel)
	users = dao.parse(data)
	if len(users) <= 0:
		return False
	user = users[0]

	cache = MouseChshRedisCache()
	cache.connect()
	cache.set(
		'MousechshUser:Id=' + user.get('Id') + ',Current[' + lang + ']', mousechsh_json_to_json(user), loginAlive
	)
	cache.close()

	return user


@mousechsh_http_router_annotation(method=['POST', 'OPTIONS'], path='/menu/get')
@mousechsh_cors_annotation()
@mousechsh_http_header_accept_language_annotation()
@MouseChshAuthorizationAnnotition()
@mousechsh_rest_api_annotation()
def menu(params):
	lang = params.get('lang', 'zh-cn')
	auth = params.get('auth', None)

	if auth is None:
		return None
	login = auth.get('data', None)
	if login is None:
		return None

	cache = MouseChshRedisCache()
	cache.connect()
	cacheobj = cache.get(
		'MousechshModule:App=' + login.get('App') + ',User=' + login.get('Id') + ',Menu[' + lang + ']'
	)
	cache.close()

	if cacheobj is not None:
		cacheobj = mousechsh_json_from_json(cacheobj)
	if cacheobj is not None:
		return cacheobj

	loginAlive = mousechsh_configuration_get('token.login.alive', 3600)

	db = MouseChshPostgresql()
	db.connect()
	data = db.get(
		'''
	   SELECT
		   A."C_Id",
		   A."C_CrOn",
		   A."C_CrBy",
		   A."C_MoOn",
		   A."C_MoBy",
		   A."C_RvOn",
		   A."C_RvBy",
		   A."C_St",
		   A."C_Owner",
		   A."C_Flag",
		   A."L_Scheme",
		   A."X_Data",
		   A."X_Property",
		   A."C_Name",
		   A."L_App_Id"     AS "C_App_Id",
		   A."L_App_Id",
		   COALESCE( B."X_Display" ->> %(lang)s, B."X_Display" ->> ( B."X_Display" ->> '' ) ) AS "L_App_Display",
		   COALESCE( A."X_Display" ->> %(lang)s, A."X_Display" ->> ( A."X_Display" ->> '' ) ) AS "C_Display",
		   A."X_Display",
		   A."L_ModuleType" AS "C_ModuleType",
		   A."L_Parent_Id",
		   A."C_Path",
		   A."C_PgCode",
		   A."C_Permission"
	   FROM "A_Application"."T_Module" A
	   INNER JOIN "A_Application"."T_App" B
		   ON A."L_App_Id"   = B."C_Id"
	   INNER JOIN "A_Application"."T_Permission" C
		   ON A."C_Id"       = C."L_Module_Id"
	   WHERE A."L_App_Id"    = %(app)s
	   AND   C."L_Common_Id" = %(user)s
	   ORDER BY A."C_Order" ASC
	   LIMIT 100
		   ''', {
			'lang': lang,
			'app': login.get('App'),
			'user': login.get('User')
		}
	)
	db.close()

	dao = MouseChshDao()
	dao.set_model(MouseChshModuleModel)
	dao.set_extra_model(MouseChshAppModel)
	modules = dao.parse(data)

	cache = MouseChshRedisCache()
	cache.connect()
	cache.set(
		'MousechshModule:App=' + login.get('App') + ',User=' + login.get('Id') + ',Menu[' + lang + ']',
		mousechsh_json_to_json(modules), loginAlive
	)
	cache.close()

	return modules


@mousechsh_http_router_annotation(method=['POST', 'OPTIONS'], path='/permission')
@mousechsh_cors_annotation()
@mousechsh_http_header_accept_language_annotation()
@mousechsh_rest_api_annotation()
def permission(params):
	pass


@mousechsh_http_router_annotation(method=['POST', 'OPTIONS'], path='/list/get')
@mousechsh_cors_annotation()
@mousechsh_http_header_accept_language_annotation()
@MouseChshAuthorizationAnnotition()
@mousechsh_rest_api_annotation()
def list(params):
	lang = params.get('lang', 'zh-cn')
	key = params.get('KeyGroup', '')

	if key == '':
		return None

	cache = MouseChshRedisCache()
	cache.connect()
	cacheobj = cache.get('MousechshKeyValue:KeyValueGroup=' + key + '[' + lang + ']')
	cache.close()

	if cacheobj is not None:
		cacheobj = mousechsh_json_from_json(cacheobj)
	if cacheobj is not None:
		return cacheobj

	loginAlive = mousechsh_configuration_get('token.login.alive', 3600)

	db = MouseChshPostgresql()
	db.connect()
	data = db.get(
		'''
	   SELECT
		   A."C_Key",
		   COALESCE( A."X_Display" ->> %(lang)s, A."X_Display" ->> ( A."X_Display" ->> '' ) ) AS "C_Display",
		   A."X_Display" AS "X_Display",
		   A."L_KeyValueGroup_Id"
	   FROM "A_Const"."W_KeyValue" A
	   INNER JOIN "A_Const"."W_KeyValueGroup" B
		   ON A."L_KeyValueGroup_Id" = B."C_Id"
	   WHERE B."C_Id"                = %(key)s
	   ORDER BY A."C_Order" ASC
	   LIMIT 100
		   ''', {
			'key': key,
			'lang': lang
		}
	)
	db.close()

	dao = MouseChshDao()
	dao.set_model(MouseChshKeyValueModel)
	kvs = dao.parse(data)

	cache = MouseChshRedisCache()
	cache.connect()
	cache.set('MousechshKeyValue:KeyValueGroup=' + key + '[' + lang + ']', mousechsh_json_to_json(kvs), loginAlive)
	cache.close()

	return kvs
